package elephant.servers.gameserver.core;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import elephant.application.BizException;
import elephant.servers.gameserver.codec.ResponseMessage;
import elephant.servers.gameserver.service.GameService;
import elephant.servers.gameserver.service.MethodInfo;
import elephant.servers.gameserver.service.ServiceContextImpl;
import elephant.servers.gameserver.service.SyncOnSession;

/**
 * 
 * @author icecooly
 *
 */
public class GameServerThreadWorker implements Runnable{
	//
	private ServiceContextImpl context;
	private GameService service; 
	private MethodInfo m;
	//
	private static Logger logger=LoggerFactory.getLogger(GameServerThreadWorker.class);
	//
	public GameServerThreadWorker(ServiceContextImpl context,GameService service,MethodInfo m){
		this.context=context;
		this.service=service;
		this.m=m;
	}

	@Override
	public void run() {
		invokeMethod();
		if(!m.disableResponse){
			context.flush();
		}
	}

	private void invokeMethod() {
		Class<?> types[]=m.method.getParameterTypes();
		Object []parameters=new Object[types.length];
		parameters[0]=context;
		boolean isException=false;
		try {
			List<String> requestParameters=context.getGameRequest().requestParameters;
			if(types.length>1){
				if(requestParameters==null){
					throw new IllegalArgumentException("parameters cannot be null");
				}
				if(requestParameters.size()<types.length-1){
					throw new IllegalArgumentException("parameters lack "+requestParameters.size()+"/"+(types.length-1));
				}
				for(int i=1;i<types.length;i++){
					Class<?> type=types[i];
					String parameter=requestParameters.get(i-1);
					if(parameter==null){
						parameters[i]=null;
						continue;
					}
					if(type.equals(String.class)){
						parameters[i]=parameter;
					}else if(type.equals(Integer.class)){
						parameters[i]=Integer.valueOf(parameter);
					}else if(type.equals(Boolean.class)){
						parameters[i]=Boolean.valueOf(parameter);
					}else if(type.equals(Short.class)){
						parameters[i]=Short.valueOf(parameter);
					}else if(type.equals(Double.class)){
						parameters[i]=Double.valueOf(parameter);
					}else if(type.equals(Long.class)){
						parameters[i]=Long.valueOf(parameter);
					}else if(type.equals(Float.class)){
						parameters[i]=Float.valueOf(parameter);
					}else{
						throw new IllegalArgumentException("parameter must be "
								+ "String/Integer/Boolean/Short/Double/Long/Float");
					}
				}
			}
			//
			if(m.method.isAnnotationPresent(SyncOnSession.class)){
				synchronized (context.getSession()) {
					m.method.invoke(service,parameters);
				}
			}else{
				m.method.invoke(service,parameters);
			}
		} catch (Exception e){
			isException=true;
			Throwable ee=e;
			if(e instanceof InvocationTargetException){
				ee=((InvocationTargetException)e).getTargetException();
			}
			int errCode=ResponseMessage.ERR_CODE_BIZ_EXCEPTION;
			if(ee instanceof BizException){
				errCode=((BizException)ee).getCode();
			}
			if(logger.isErrorEnabled()){
				logger.error(e.getMessage(),e);
			}
			context.sendError(
					errCode,
					ee.getMessage());
		}finally{
			long endTime=System.currentTimeMillis();
			long useTime=endTime-context.getGameRequest().startTime;
			if(logger.isInfoEnabled()){
				logger.info("<<<invokeMethod:{}@{}<{} useTime:{}",
						context.getGameRequest().requestId,
						context.getSession().getPrincipal(),
						context.getServiceId(),
						useTime);
			}
			context.getGameServer().getPerformMonitor().invokeMethod(useTime,isException);
		}
		
	}
}
